﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace Programmer_1
{
    public partial class AppForm : Form
    {
        AppModel app = new AppModel();

        int x0 = 0;
        List<int> tempList = new List<int>();
        List<int> lightList = new List<int>();
        BindingList<string> cbPortsBindingList = new BindingList<string>();

        bool getPortsInProgress;

        public AppForm()
        {
            InitializeComponent();

            // using BindingSource for ComboBox UI to get automatically updated 
            // when putting items in DataSource
            BindingSource cbPortsBindingSource = new BindingSource();
            cbPortsBindingSource.DataSource = cbPortsBindingList;
            cbPorts.DataSource = cbPortsBindingSource;

            // listen to events
            app.StateChanged += OnProgrammerStateChanged;
            app.Connected += OnProgrammerConnected;
            app.Disconnected += OnProgrammerDisconnected;
            app.DataRead += UpdateChart; // to process read data
            app.DataLogger.StateChanged += OnDataLoggerStateChanged;
            app.TempAlarm.MinTempAlarm += OnMinTempAlarm; // to process min threshold overflow
            app.TempAlarm.MaxTepmAlarm += OnMaxTempAlarm; // to process max threshold overflow
            app.TempAlarm.StateChanged += OnTempAlarmStateChanged;

            // StateChanged event not being fired upon object create. Update controls manually.
            EnableDisableControls(app.CurrentState);
            EnableDisableLogDataControls(app.DataLogger.CurrentState);
            EnableDisableTempAlarmControls(app.TempAlarm.CurrentState);
        }

        private async void btnGetPorts_Click(object sender, EventArgs e)
        {
            getPortsInProgress = true;
            try
            {
                clearStatus();
                // clear port list
                cbPortsBindingList.Clear();
                // manually update control state (to empty ports combobox)
                EnableDisableControls(app.CurrentState);
                AppModel.Result result = await app.GetPortsAsync();
                if (!result.success)
                {
                    OnError("GetPorts: " + result.strError);
                }
                // populate port list
                foreach (string port in result.Ports)
                {
                    cbPortsBindingList.Add(port);
                }
            }
            finally
            {
                getPortsInProgress = false;
                // manually update control state (to update ports combobox)
                EnableDisableControls(app.CurrentState);
            }
        }

        private async void btnOpenPort_Click(object sender, EventArgs e)
        {
            clearStatus();
            AppModel.Result result = await app.OpenPortAsync((string)cbPorts.SelectedItem);
            if (!result.success)
            {
                OnError("OpenPort: " + result.strError);
            }
        }

        private async void btnClosePort_Click(object sender, EventArgs e)
        {
            clearStatus();
            AppModel.Result result = await app.ClosePortAsync();
            if (!result.success)
            {
                OnError("ClosePort: " + result.strError);
            }
        }

        private async void btnPowerOn_Click(object sender, EventArgs e)
        {
            clearStatus();
            AppModel.Result result = await app.PowerOnAsync();
            if (!result.success)
            {
                OnError("PowerOn: " + result.strError);
            }
        }

        private async void btnPowerOff_Click(object sender, EventArgs e)
        {
            clearStatus();
            AppModel.Result result = await app.PowerOffAsync();
            if (!result.success)
            {
                OnError("PowerOff: " + result.strError);
            }
        }

        private void btnReadData_Click(object sender, EventArgs e)
        {
            clearStatus();
            x0 = 0; // reset
            app.ReadDataAsync(Convert.ToInt32(udSamplesPerSecond.Value), OnError);
        }

        private void btnStopReadData_Click(object sender, EventArgs e)
        {
            app.StopReadData();
        }

        private void btnLogData_Click(object sender, EventArgs e)
        {
            fdSaveLogData.ShowDialog();
            Debug.WriteLine("logging to file {0}", fdSaveLogData.FileName.ToString());

            if (fdSaveLogData.fdSaveLogData.FileName != "")
            {
                app.DataLogger.LogDataAsync(fdSaveLogData.FileName);
            }
        }

        private void btnStopLogData_Click(object sender, EventArgs e)
        {
            app.DataLogger.StopLogData();
        }

        void UpdateChart(int temp, int light, int rate)
        {
            doInUiThread(() =>
            {
                // shift to left if necessary
                for (int i = 0, n = tempList.Count, spf = Convert.ToInt32(udSamplesPerFrame.Value); i < n - spf; i++)
                {
                    tempList.RemoveAt(0);
                    lightList.RemoveAt(0);
                    x0++;
                }

                // add data to list
                tempList.Add(temp);
                lightList.Add(light);

                bool displayAlarms = cbDisplayAlarms.Checked;

                // (re)build chart
                chartTempLight.SuspendLayout();
                chartTempLight.Series["Temperature"].Points.Clear();
                chartTempLight.Series["Light"].Points.Clear();
                chartTempLight.Series["Min Temperature"].Points.Clear();
                chartTempLight.Series["Max Temperature"].Points.Clear();
                for (int i = 0, j = x0; i < tempList.Count; i++, j++)
                {
                    chartTempLight.Series["Temperature"].Points.AddXY(j, tempList.ElementAt(i));
                    chartTempLight.Series["Light"].Points.AddXY(j, lightList.ElementAt(i));
                    if (displayAlarms)
                    {
                        // min/max threshold lines
                        chartTempLight.Series["Min Temperature"].Points.AddXY(j, Convert.ToInt32(udMinTemp.Value));
                        chartTempLight.Series["Max Temperature"].Points.AddXY(j, Convert.ToInt32(udMaxTemp.Value));
                    }
                }
                chartTempLight.ResumeLayout();

                tbRate.Text = "" + rate;
            });
        }

        void OnMinTempAlarm(int temp, DateTime time)
        {
            doInUiThread(() => 
            {
                this.tbMinTempValue.Text = "" + temp;
                this.tbLastMinTempTime.Text = time.ToString("H-mm-ss-fff");
            });
        }

        void OnMaxTempAlarm(int temp, DateTime time)
        {
            doInUiThread(() =>
            {
                this.tbMaxTempValue.Text = "" + temp;
                this.tbLastMaxTempTime.Text = time.ToString("H-mm-ss-fff");
            });
        }

        private void cbDisplayAlarms_CheckedChanged(object sender, EventArgs e)
        {
            if (cbDisplayAlarms.Checked)
            {
                // clear recent values
                this.tbMinTempValue.Text = null;
                this.tbMaxTempValue.Text = null;
                this.tbLastMinTempTime.Text = null;
                this.tbLastMaxTempTime.Text = null;

                fdSaveTempAlarmData.ShowDialog();
                if (fdSaveTempAlarmData.FileName != "")
                {
                    app.TempAlarm.TurnOnAsync(Convert.ToInt32(udMinTemp.Value), Convert.ToInt32(udMaxTemp.Value), fdSaveTempAlarmData.FileName);
                }
            }
            else
            {
                app.TempAlarm.TurnOff();
            }
        }

        void OnProgrammerStateChanged(AppModel.State oldState, AppModel.State newState)
        {
            doInUiThread(() =>
            {
                EnableDisableControls(newState);
            });
        }

        void OnDataLoggerStateChanged(DataLogger.State oldState, DataLogger.State newState)
        {
            doInUiThread(() =>
            {
                EnableDisableLogDataControls(newState);
            });
        }

        void OnTempAlarmStateChanged(TempAlarm.State oldState, TempAlarm.State newState)
        {
            doInUiThread(() =>
            {
                EnableDisableTempAlarmControls(newState);
            });
        }

        void OnProgrammerConnected(string progID)
        {
            doInUiThread(() =>
            {
                btnGetPorts_Click(null, null);
                tbStatus.Text = "Device connected";
            });
        }

        void OnProgrammerDisconnected(string progID)
        {
            doInUiThread(() =>
            {
                btnGetPorts_Click(null, null);
                tbStatus.Text = "Device disconnected";
            });
        }

        void clearStatus()
        {
            tbStatus.Text = null;
        }

        void OnError(string strError)
        {
            doInUiThread(() =>
            {
                tbStatus.Text = strError;
            });
        }

        void EnableDisableControls(AppModel.State state)
        {
            this.btnGetPorts.Enabled = ((state & (AppModel.State.UNKNOWN | AppModel.State.CONNECTED)) > 0) && !getPortsInProgress;
            this.cbPorts.Enabled = ((state & (AppModel.State.UNKNOWN | AppModel.State.CONNECTED)) > 0) && !getPortsInProgress;
            this.btnOpenPort.Enabled = (state == AppModel.State.CONNECTED) && (cbPortsBindingList.Count > 0) && !getPortsInProgress;
            this.btnClosePort.Enabled = state == AppModel.State.PORT_OPEN;
            this.btnPowerOn.Enabled = state == AppModel.State.PORT_OPEN;
            this.btnPowerOff.Enabled = state == AppModel.State.POWERED_ON;
            this.btnReadData.Enabled = state == AppModel.State.POWERED_ON;
            this.btnStopReadData.Enabled = state == AppModel.State.READING_DATA;
            this.udSamplesPerSecond.Enabled = state != AppModel.State.READING_DATA;
        }

        void EnableDisableLogDataControls(DataLogger.State state)
        {
            //this.btnLogData.Enabled = (this.app.CurrentState == AppModel.State.READING_DATA) && (state == DataLogger.State.LOGGING_OFF);
            this.btnStopLogData.Enabled = state == DataLogger.State.LOGGING_ON;
        }

        void EnableDisableTempAlarmControls(TempAlarm.State state)
        {
            cbDisplayAlarms.Checked = state == TempAlarm.State.TURNED_ON;
        }

        void doInUiThread(Action action)
        {
            if (disposing)
            {
                // E.g. worker thread continues pumping data when window gets closed
                return;
            }
            if (InvokeRequired)
            {
                BeginInvoke(action);
                return;
            }
            action();
        }
    }
}
